/*
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 */
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <netinet/ip.h>

#define MY_DEST_MAC0    0x01
#define MY_DEST_MAC1    0x00
#define MY_DEST_MAC2    0x5e
#define MY_DEST_MAC3    0x00
#define MY_DEST_MAC4    0x00
#define MY_DEST_MAC5    0x01

#define DEFAULT_IF      "eth0"
#define BUF_SIZ         1024

int main(int argc, char *argv[])
{
        int i = 0;
        int sockfd;
        struct ifreq if_idx;
        struct ifreq if_mac;
        int tx_len = 0;
        char sendbuf[BUF_SIZ];
        struct ether_header *eh = (struct ether_header *) sendbuf;
        struct iphdr *iph = (struct iphdr *) (sendbuf + sizeof(struct ether_header));
        struct sockaddr_ll socket_address;
        char ifName[IFNAMSIZ];

        /* Get interface name */
        if (argc > 1)
                strcpy(ifName, argv[1]);
        else
                strcpy(ifName, DEFAULT_IF);

        /* Open RAW socket to send on */
        if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1) {
            perror("socket");
        }

        /* Get the index of the interface to send on */
        memset(&if_idx, 0, sizeof(struct ifreq));
        strncpy(if_idx.ifr_name, ifName, IFNAMSIZ-1);
        if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0)
            perror("SIOCGIFINDEX");
        /* Get the MAC address of the interface to send on */
        memset(&if_mac, 0, sizeof(struct ifreq));
        strncpy(if_mac.ifr_name, ifName, IFNAMSIZ-1);
        if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0)
            perror("SIOCGIFHWADDR");


        struct ifreq if_ip;
         memset(&if_ip, 0, sizeof(struct ifreq));


/* Construct the Ethernet header */
        memset(sendbuf, 0, BUF_SIZ);
/* Ethernet header */
        eh->ether_shost[0] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[0];
        eh->ether_shost[1] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[1];
        eh->ether_shost[2] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[2];
        eh->ether_shost[3] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[3];
        eh->ether_shost[4] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[4];
        eh->ether_shost[5] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[5];
        eh->ether_dhost[0] = MY_DEST_MAC0;
        eh->ether_dhost[1] = MY_DEST_MAC1;
        eh->ether_dhost[2] = MY_DEST_MAC2;
        eh->ether_dhost[3] = MY_DEST_MAC3;
        eh->ether_dhost[4] = MY_DEST_MAC4;
        eh->ether_dhost[5] = MY_DEST_MAC5;
        /* Ethertype field */
        eh->ether_type = htons(ETH_P_IP);
        tx_len += sizeof(struct ether_header);

/* IP Header */
        iph->ihl = 5;
        iph->version = 4;
        iph->tos = 16; // Low delay
        iph->id = htons(54321);
        iph->ttl = 1; // hops
        iph->protocol = 2; // IGMP
        /* Source IP address, can be spoofed */
         iph->saddr = inet_addr("0.0.0.0");
        /* Destination IP address */
        iph->daddr = inet_addr("224.0.0.1");
        tx_len += sizeof(struct iphdr);


        unsigned short csum(unsigned short *buf, int nwords)
        {
            unsigned long sum;
                for(sum=0; nwords>0; nwords--)
                sum += *buf++;
                sum = (sum >> 16) + (sum &0xffff);
                sum += (sum >> 16);
                return (unsigned short)(~sum);
        }

/* IGMP header data */
        sendbuf[tx_len++] = 0x11;
        sendbuf[tx_len++] = 0x64;
        sendbuf[tx_len++] = 0xee;
        sendbuf[tx_len++] = 0x9b;
        sendbuf[tx_len++] = 0x0;
        sendbuf[tx_len++] = 0x0;
        sendbuf[tx_len++] = 0x0;
        sendbuf[tx_len++] = 0x0;

/* Length of IP payload and header */
        iph->tot_len = htons(tx_len - sizeof(struct ether_header));
/* Calculate IP checksum on completed header */
        iph->check = csum((unsigned short *)(sendbuf+sizeof(struct ether_header)), sizeof(struct iphdr)/2);

/* Add Ethernet padding to the end of packet */
        for(i=0;i<18;i++){
                sendbuf[tx_len++] = 0x0;
        }


/* Index of the network device */
        socket_address.sll_ifindex = if_idx.ifr_ifindex;
/* Address length*/
        socket_address.sll_halen = ETH_ALEN;
/* Destination MAC */
        socket_address.sll_addr[0] = MY_DEST_MAC0;
        socket_address.sll_addr[1] = MY_DEST_MAC1;
        socket_address.sll_addr[2] = MY_DEST_MAC2;
        socket_address.sll_addr[3] = MY_DEST_MAC3;
        socket_address.sll_addr[4] = MY_DEST_MAC4;
        socket_address.sll_addr[5] = MY_DEST_MAC5;

/* Send packet */

        for(;;) {
        if (sendto(sockfd, sendbuf, tx_len, 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll)) < 0)
            printf("Send failed\n");

        sleep(15);
        }


        return 0;
}

